/**
 * 
 */
package team7.made;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;


/**
 * Singleton instance containing questions and answers
 * 
 * @author Team 7
 *
 */
public final class QuestionsDBase {

	public static QuestionsDBase INSTANCE = new QuestionsDBase(); 
	/**
	 * private constructor for a Singleton instance
	 */
	private QuestionsDBase() {
		
	}
	
	private Map<Integer, Question> questions = new HashMap<Integer, Question>();
	
	// load questions xml file
	// implement controller to return a populated view of the question
	// 
	
	public void init(InputStream xmlFilePath) {
		buildQuestionsDBase(xmlFilePath);
	}
		
	/**
	 * @return
	 */
	private Document parseQuestionsXML(InputStream xmlFilePath) {
		// file present within jar.
		SAXBuilder documentBuilder = new SAXBuilder();
		try {
			Document document = documentBuilder.build(xmlFilePath);
			process(document);
			//process(document, null);
			System.out.println("done");
		}  catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JDOMException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	// build questions data-base.
	private void buildQuestionsDBase(InputStream xmlFilePath) {
		Document document = parseQuestionsXML(xmlFilePath);
		if(document != null) {
			process(document);
		}
		else {
			// throw an exception
		}
	}
	
	/* process DOM tree using walker */
	@SuppressWarnings("unchecked")
	private void process(Document document) {
//		TreeWalker walker = ((DocumentTraversal) document).createTreeWalker(
//				document.getDocumentElement(), NodeFilter.SHOW_ALL, null,
//				true);
		Element root = document.getRootElement();
		List children = root.getChildren("Question");
		// iterate through all children
		Question question = null;
		for (Object n : children) {

			if (n instanceof Element) {
				// add question to dbase
				// create new question instance
				Element questionElement = (Element) n;
				System.out.println(n);
				question = new Question();
				question.setId(questionElement.getAttributeValue("id"));
				processQuestionElement(questionElement, question);
				this.questions
						.put(Integer.parseInt(question.getId()), question);
				question = null;
			}
		}
	}
	
	/**
	 * build a {@link Question} instance out of <Question> tag.
	 * 
	 * @param qElement - {@link Element} instance of Question
	 * @param question
	 */
	private void processQuestionElement(Element qElement, Question question) {
		Element statement = qElement.getChild("statement");
		String qStatement = statement.getText();
		question.setQuestionStatement(qStatement);
		// process options
		Element optionsElement = qElement.getChild("options");
		int i = 0;
		List options = optionsElement.getChildren();
		while(i < options.size()) {
			Object option = options.get(i);
			if(option instanceof Element) {
				question.getOptionsMap().put(((Element) option).getName(), ((Element) option).getText());
			}
			i++;
		}
		// process answer
		Element answerElement = qElement.getChild("answer");
		question.setCorrectOption(answerElement.getText());
	}
	
	/*
	 * getters and setters for questions
	 */
	/**
	 * @param id - unique id number of the question
	 * @return Question instance.
	 */
	public synchronized Question getQuestion(Integer id) {
		return questions.get(id);
	}
	
	// function to return a question at random.
	private Random randomQuestionIdGenerator = new Random();
	
	// pool of already visited questions
	private Map<Integer, Boolean> visitedQuestions = new HashMap<Integer, Boolean>();	
	
	/**
	 * function returns different questions from the DBase each time.
	 * a null is returned when all the questions are exhausted.
	 * 
	 * @return a {@link Question} instance at random.
	 *         null if all questions are returned already.
	 */
	public Question nextRandomQuestion() {
		// limit number generated to number of questions in the current test.
		int numberOfQuestions = questions.size();
		final int upperBound = numberOfQuestions + 1;
		int nextQuestionId = -1;
		while(visitedQuestions.size() < numberOfQuestions) { // loop until u find a question number already present in dbase
		    nextQuestionId = randomQuestionIdGenerator.nextInt(upperBound);
		    if(questions.containsKey(nextQuestionId)) {
		    	Boolean visitedQuestion = visitedQuestions.get(nextQuestionId);
				if(visitedQuestion == null) {
		    		break;
		    	}
		    	else if(visitedQuestion != null &&
		    			!visitedQuestion) {
		    		break;
		    	}
		    }
		}
	    // found question
		Question question = questions.get(nextQuestionId);
		if(question == null) {
			// no new question to return
			// clear data structures.
			visitedQuestions.clear();
			// TODO check if the random number generator also needs to be reset.
		}
		else {
			visitedQuestions.put(nextQuestionId, Boolean.TRUE);
		}
		return question;
	}
	
}
